package com.lcache.common.redis;

import io.lettuce.core.api.StatefulRedisConnection;
import io.lettuce.core.api.sync.RedisCommands;

/**
 * @program: lcache
 * @description:
 * @author: Alex Wu
 * @createDate: 2025-04-22 11:48
 **/
public class RedisLock {

    private final RedisService redisService;

    public RedisLock(RedisService redisService) {
        this.redisService = redisService;
    }

    /**
     * 尝试获取锁
     * @param key 锁 key
     * @param value 唯一值（如 UUID，表示锁的持有者）
     * @param expireSec 过期时间，避免死锁
     * @return true：成功，false：失败
     */
    public boolean tryLock(String key, String value, long expireSec) {
        StatefulRedisConnection<String, String> connection = null;
        try {
            connection = RedisClientManager.getConnection();
            RedisCommands<String, String> commands = connection.sync();
            String result = commands.set(key, value, io.lettuce.core.SetArgs.Builder.nx().ex(expireSec));
            return "OK".equals(result);
        } catch (Exception e) {
            throw new RuntimeException("tryLock error", e);
        } finally {
            if (connection != null) {
                RedisClientManager.returnConnection(connection);
            }
        }
    }

    /**
     * 释放锁
     * @param key 锁 key
     * @param value 锁持有者的 value，用于校验是否是当前线程持有
     */
    public boolean unlock(String key, String value) {
        String luaScript =
                "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                        "  return redis.call('del', KEYS[1]) " +
                        "else " +
                        "  return 0 " +
                        "end";
        StatefulRedisConnection<String, String> connection = null;
        try {
            connection = RedisClientManager.getConnection();
            RedisCommands<String, String> commands = connection.sync();
            Object result = commands.eval(luaScript, io.lettuce.core.ScriptOutputType.INTEGER,
                    new String[]{key}, value);
            return Long.valueOf(1).equals(result);
        } catch (Exception e) {
            throw new RuntimeException("unlock error", e);
        } finally {
            if (connection != null) {
                RedisClientManager.returnConnection(connection);
            }
        }
    }

    /**
     * 阻塞等待获取锁（带超时）
     */
    public boolean tryLockWithWait(String key, String value, long expireSec, long waitMillis, long retryIntervalMillis) {
        long endTime = System.currentTimeMillis() + waitMillis;
        while (System.currentTimeMillis() < endTime) {
            if (tryLock(key, value, expireSec)) {
                return true;
            }
            try {
                Thread.sleep(retryIntervalMillis);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
                break;
            }
        }
        return false;
    }
}
